﻿using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;
using System;
using System.Linq;
using Wicture.DbRESTFul;
using Wicture.EPC.OnlineQueryProxy.Agents;
using Wicture.EPC.OnlineQueryProxy.Processors;

namespace Wicture.EPC.OnlineQueryProxy.Proxy
{
    public class OnlineQueryRequestHandler : IOnlineQueryRequestHandler
    {
        private readonly IQueryProcessor[] _queryProcessors;
        private readonly IAgentManager _agentManager;
        private readonly IHubContext<OnlineQueryServiceHub> _hubContext;

        public OnlineQueryRequestHandler(IQueryProcessor[] queryProcessors, IAgentManager agentManager, IHubContext<OnlineQueryServiceHub> hubContext)
        {
            _queryProcessors = queryProcessors;
            _agentManager = agentManager;
            _hubContext = hubContext;
        }

        public object Query(QueryRequest request)
        {
            try
            {
                LoggerManager.Logger.LogInformation($"Query for {request.Task}: {request.ParamString}.");

                var processor = _queryProcessors.FirstOrDefault(p => p.Task == request.Task && p.Match(request.ParamString));
                if (processor == null)
                {
                    LoggerManager.Logger.LogError($"Failed to query for {request.Task.ToString()} : { request.RequestId }. {Environment.NewLine} 未找到匹配的processor");
                    throw new LogicalException("未找到匹配的processor", 619);
                }

                LoggerManager.Logger.LogInformation($"processor=>{processor.ProcessorName}被选中");

                var agent = _agentManager.GetAgentByProcessorName(processor.ProcessorName);

                LoggerManager.Logger.LogInformation($"代理服务器{agent.Name} 被选中.");

                using (var context = QueryContextManager.CreateQueryContext(processor, agent, request))
                {
                    // 如题没有创建成功，则说明用户可能连续提交了同一VIN的查询请求，直接返回Busy即可。
                    if (context == null)
                    {
                        LoggerManager.Logger.LogError($"Failed to query for {request.Task.ToString()} : { request.RequestId }. {Environment.NewLine} busy");
                        throw new LogicalException("Busy", 666);
                    }
                    try
                    {
                        SendQueryRequest(agent, request);

                        if (context.AutoResetEvent.WaitOne(processor.QueryTimeOut))
                        {
                            return context.Processor.ProcessQueryResponse(context.Result);
                        }
                        else
                        {
                            LoggerManager.Logger.LogError($"Failed to query for {request.Task.ToString()} : { request.RequestId }. {Environment.NewLine}Timeout");
                            throw new LogicalException("Timeout", 666);
                        }
                    }
                    catch (Exception ex)
                    {
                        LoggerManager.Logger.LogError($"Failed to query for {request.Task.ToString()} : { request.RequestId }. {Environment.NewLine} {ex.Message}");
                        throw new LogicalException(ex.Message, 666);
                    }
                }
            }
            catch (Exception exx)
            {
                LoggerManager.Logger.LogError($"Failed to query for {request.Task.ToString()} : { request.RequestId }. {Environment.NewLine} {exx.Message}");
                throw exx;
            }
        }

        private void SendQueryRequest(Agent agent, QueryRequest request)
        {
            _hubContext.Clients.Client(agent.ConnectionId).SendAsync("queryRequest", request);
        }

        public void RefreshAgentStatus(string processorName)
        {
            if (string.IsNullOrEmpty(processorName))
            {
                _hubContext.Clients.All.SendAsync("updateAgent");
                return;
            }

            var connectionIds = AgentCollection.Agents
                .Where(a => a.ProcessorName.Equals(processorName, StringComparison.InvariantCultureIgnoreCase))
                .Select(a => a.ConnectionId)
                .ToList();

            _hubContext.Clients.Clients(connectionIds).SendAsync("updateAgent");
        }
    }
}
